/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: port io driver
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-06-11     JasonHu           Init
 */

#define NX_LOG_NAME "port io"
#include <base/log.h>

#ifndef CONFIG_NX_DRIVER_PIO
#define CONFIG_NX_DRIVER_PIO
#endif

#ifdef CONFIG_NX_DRIVER_PIO

#include <base/driver.h>
#include <base/debug.h>
#include <base/uaccess.h>
#include <io.h>

#define DRV_NAME "port io driver"
#define DRV_VERSION "0.1"

#define DEV_NAME "pio"

#define MAX_PORTS_ID 65535

NX_PRIVATE NX_Error PIO_Read(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    NX_U32 data;

    if (off < 0 || off > MAX_PORTS_ID) /* valid port */
    {
        return NX_EINVAL;
    }

    switch (len)
    {
    case 1:
        data = IO_In8(off);
        NX_CopyToUser(buf, (char *)&data, len);
        break;
    case 2:
        data = IO_In16(off);
        NX_CopyToUser(buf, (char *)&data, len);
        break;
    case 4:
        data = IO_In32(off);
        NX_CopyToUser(buf, (char *)&data, len);
        break;
    
    default:
        return NX_EINVAL;
    }

    if (outLen)
    {
        *outLen = len;
    }
    return NX_EOK;
}

NX_PRIVATE NX_Error PIO_Write(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    NX_U32 data = 0;

    if (off < 0 || off > MAX_PORTS_ID) /* valid port */
    {
        return NX_EINVAL;
    }

    switch (len)
    {
    case 1:
        NX_CopyFromUser((char *)&data, buf, len);
        IO_Out8(off, data);
        break;
    case 2:
        NX_CopyFromUser((char *)&data, buf, len);
        IO_Out16(off, data);
        break;
    case 4:
        NX_CopyFromUser((char *)&data, buf, len);
        IO_Out32(off, data);
        break;
    
    default:
        return NX_EINVAL;
    }

    if (outLen)
    {
        *outLen = len;
    }
    return NX_EOK;
}

NX_PRIVATE NX_DriverOps PIO_DriverOps = {
    .read       = PIO_Read,
    .write      = PIO_Write,
};

NX_PRIVATE void PIO_DriverInit(void)
{
    NX_Device *device;

    NX_Driver *driver = NX_DriverCreate(DRV_NAME, NX_DEVICE_TYPE_SCREEN, 0, &PIO_DriverOps);
    if (driver == NX_NULL)
    {
        NX_LOG_E("create driver failed!");
        return;
    }

    if (NX_DriverAttachDevice(driver, DEV_NAME, &device) != NX_EOK)
    {
        NX_LOG_E("attach device %s failed!", DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }

    if (NX_DriverRegister(driver) != NX_EOK)
    {
        NX_LOG_E("register driver %s failed!", DRV_NAME);
        NX_DriverDetachDevice(driver, DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }
}

NX_PRIVATE void PIO_DriverExit(void)
{
    NX_DriverCleanup(DRV_NAME);
}

NX_DRV_INIT(PIO_DriverInit);
NX_DRV_EXIT(PIO_DriverExit);

#endif /* CONFIG_NX_DRIVER_PIO */
